#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h>

#include <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H

#define FB_DEV  "/dev/fb0"

#define argb8888_to_rgb565(color)   ({\
                                      unsigned int temp = (color); \
                                      ((temp & 0xF80000) >> 8) |\
                                      ((temp & 0xFC00) >> 5) |\
                                      ((temp & 0xF8) >> 3); \
                                      })

static unsigned int width;
static unsigned int height;
static unsigned short *screen_base = NULL;
static unsigned long screen_size;
static int fd = -1;

static FT_Library library;
static FT_Face face;

static int fb_dev_init(void)
{
    struct fb_var_screeninfo fb_var = {0};
    struct fb_fix_screeninfo fb_fix = {0};

    fd = open(FB_DEV, O_RDWR);
    if (fd < 0) {
        fprintf(stderr, "open error %s: %s\n", FB_DEV, strerror(errno));
        return -1;
    }

    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

    screen_size = fb_fix.line_length * fb_var.yres;
    width = fb_var.xres;
    height = fb_var.yres;

    screen_base = mmap(NULL, screen_size, PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if ((void *)screen_base == MAP_FAILED) {
        perror("mmap error\n");
        close(fd);
        return -1;
    }

    memset(screen_base, 0xFF, screen_size);
    return 0;
}

static int freetype_init(const char *font, int angle) 
{
    FT_Error error;
    FT_Vector pen;
    FT_Matrix matrix;
    float rad;

    FT_Init_FreeType(&library);

    error = FT_New_Face(library, font, 0, &face);
    if (error) {
        fprintf(stderr, "FT_New_Face error:%d\n", error);
        exit(EXIT_FAILURE);
    }

    pen.x = 0 * 64;
    pen.y = 0 * 64;

    rad = (1.0 * angle/180)*M_PI;
#if 0 //非水平方向
    matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);
    matrix.xy = (FT_Fixed)(-sin(rad) * 0x10000L);
    matrix.yx = (FT_Fixed)( sin(rad) * 0x10000L);
    matrix.yy = (FT_Fixed)( cos(rad) * 0x10000L);
#endif
#if 1 //斜体 水平方向显示的
    matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);
    matrix.xy = (FT_Fixed)( sin(rad) * 0x10000L);
    matrix.yx = (FT_Fixed)( 0 * 0x10000L);
    matrix.yy = (FT_Fixed)( 1 * 0x10000L);
#endif

   FT_Set_Transform(face, &matrix, &pen);
   FT_Set_Pixel_Sizes(face, 50, 0) ;

   return 0;
}
static void lcd_draw_character(int x, int y, const wchar_t *str, unsigned int color)
{
    unsigned rgb565_clolr = argb8888_to_rgb565(color);
    FT_GlyphSlot slot = face->glyph;
    size_t len = wcslen(str);
    long int temp;
    int n;
    int i, j, p, q;
    int max_x, max_y, start_x, start_y;

    for (n=0; n<len; n++) {
        if (FT_Load_Char(face, str[n], FT_LOAD_RENDER)) {
            continue;
        }

        start_y = y - slot->bitmap_top;
        if (start_y < 0) {
            q = -start_y;
            temp = 0;
            j = 0;
        } else {
            q = 0;
            temp = width * start_y;
            j = start_y;
        }

        max_y = start_y + slot->bitmap.rows;
        if (max_y > (int)height) {
            max_y = height;
        }

        for (; j < max_y; j++,q++, temp+=width) {
            start_x = x + slot->bitmap_left;
            if (start_x < 0) {
                p = -start_x;
                i = 0;
            } else {
                p = 0;
                i = start_x;
            }

            max_x = start_x + slot->bitmap.width;
            if (max_x > (int)width) {
                max_x = width;
            }

            for (; i<max_x; i++, p++) {
                if (slot->bitmap.buffer[q*slot->bitmap.width+p]) {
                    screen_base[temp+i] = rgb565_clolr;
                }
            }
        }

        x += slot->advance.x / 64;
        y += slot->advance.y / 64;
    }
}


int main(int argc, char *argv[])
{
    /* LCD 初始化 */
    if (fb_dev_init())
        exit(EXIT_FAILURE);

    /* freetype 初始化 */
    if (freetype_init(argv[1], atoi(argv[2])))
        exit(EXIT_FAILURE);

    /* 在 LCD 上显示中文 */
    int y = height * 0.25;
    lcd_draw_character(50, 100, L"路漫漫其修远兮，吾将上下而求索", 0x000000);
    lcd_draw_character(50, y+100, L"莫愁前路无知己，天下谁人不识君", 0x9900FF);
    lcd_draw_character(50, 2*y+100, L"君不见黄河之水天上来，奔流到海不复回", 0xFF0099);
    lcd_draw_character(50, 3*y+100, L"君不见高堂明镜悲白发，朝如青丝暮成雪", 0x9932CC);
    
    /* 退出程序 */
    FT_Done_Face(face);
    FT_Done_FreeType(library);
    munmap(screen_base, screen_size);
    close(fd);
    exit(EXIT_SUCCESS);
}